package com.justinbuser.singleton
{
	import flash.desktop.NativeApplication;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.filesystem.*;
	import flash.net.ObjectEncoding;
	import flash.net.SharedObject;
	import flash.net.registerClassAlias;
	import flash.system.System;
	import flash.utils.Proxy;
	import flash.utils.flash_proxy;
	
	import mx.core.FlexGlobals;
	import mx.events.PropertyChangeEvent;
	import mx.utils.ObjectUtil;
	
	use namespace flash_proxy;
	
	[Event(name="propertyChange", type="mx.events.PropertyChangeEvent")]
	[Event(name="instanceChanged", type="flash.events.Event")]
	
	[Bindable("propertyChange")] 
	public dynamic class Controller extends Proxy implements IEventDispatcher
	{
		protected static var instance:*;
		
		public function Controller(target:IEventDispatcher=null, classes:Vector.<Class>=null){
			if(!instance)
			{
				super();
					version = "version";
					versionLabel = "label";
					appID = "com.justinbuser";
					locSo = SharedObject.getLocal(appID,"/");
					dispatcher = new EventDispatcher(this);
					instance = this;
			}
		}
		
		protected function init(classes:Vector.<Class>=null):void{
			if(classes)
			{
				for(var i:int = 0; i < classes.length; i++)
				{
					registerClass(classes[i]);
				}
			}
			log.info('Controller created');
			var error:String;
			if(!webBased){
				trace('not web based');
				var file:flash.filesystem.File = flash.filesystem.File.applicationStorageDirectory.resolvePath(locSoPath+"/deleteme.sol");
				if(file.exists)
				{
					trace('deleteme shared object found, removing');
					file.deleteFile();
					file = flash.filesystem.File.applicationStorageDirectory.resolvePath(locSoPath+"/"+appID+".sol");
					if(file.exists)
					{
						file.deleteFile();
					}
				}
				
				
				try{
					locSo = SharedObject.getLocal(appID,"/");
				}
				catch(ae:ArgumentError){
					error = "Corrupted local stored preferences/data detected, creating new local store";
					trace(ae);
					locSo = null;
					file = flash.filesystem.File.applicationStorageDirectory.resolvePath(locSoPath+"/"+appID+".sol");
					if(file.exists)
					{
						file.deleteFile();
					}
					locSo = SharedObject.getLocal(appID,"/");
				}
				catch(e:*){
					trace(e);
					error = "Corrupted local storage detected, unable to rebuild. Please exit the program and delete the files located in: "+locSoPath;
					locSo = SharedObject.getLocal("deleteme","/");
				}
				file = null;
			}
			else
			{
				locSo = SharedObject.getLocal(appID,"/");
			}
			if(locSo){
				locSo.objectEncoding = ObjectEncoding.DEFAULT;
				saveProperties({applicationId:appID});
				dispatchEvent(new Event('propertyChange'));
			}
			
			if(error)
			{
				Core.status = error;
				return;
			}
			
		}

		public function registerClass(classToRegister:Class):String{
			var className:String = String(ObjectUtil.getClassInfo(classToRegister).name).split(':').pop();
			registerClassAlias(className,classToRegister);
			log.info("Registered class:",className);
			return className;
		}
		
		public function removeProperty(name:*):void{
			delete locSo.data[name];
		}
		/*protected function hasSetting(s:String):Boolean{
			return locSo.data && locSo.data.hasOwnProperty(s) ? true : false;
		}
		
		protected function getSetting(s:String):*{
			return locSo.data[s];
		}*/
		
		protected function saveProperties(properties:*):Boolean{
			for(var property:String in properties){
				var oldProperty:Object = this[property] ? this[property].valueOf() : null;
				var propE:PropertyChangeEvent = PropertyChangeEvent.createUpdateEvent(this,property,oldProperty,properties[property]);
				locSo.setProperty(property,properties[property]);
				dispatchEvent(propE);
			}
			return flushSo();
		}
		
		private function flushSo():Boolean{
			try {
				var s:String = locSo.flush(1000);
				return true;
			} 
			catch (error:Error) {
				log.error(error.message);
			}
			return false;
		}
		
		public function clearSettings():void{
			locSo.clear();
			locSo.objectEncoding = ObjectEncoding.DEFAULT;
			flushSo();
		}
		
		flash_proxy override function callProperty(name:*, ...rest):* 
		{ 
			try 
			{ 
				log.info('calling:',name,rest);
			} 
			catch (e:Error) 
			{ 
				log.error('error calling:',name,rest); 
			} 
		} 

		flash_proxy override function getProperty(name:*):*
		{
			return locSo.data[name];
		}
		
		flash_proxy override function hasProperty(name:*):Boolean
		{
			return locSo.data.hasOwnProperty(name);
		}
		
		flash_proxy override function setProperty(name:*, value:*):void
		{
			name = String(name);
			var oldProperty:Object = locSo.data[name] ? locSo.data[name].valueOf() : null;
			var propE:PropertyChangeEvent = PropertyChangeEvent.createUpdateEvent(this,name,oldProperty,value);
			locSo.setProperty(name,value);
			if(value is Array){
				log.info("Saving array/vector, flushing so");
				var flushed:Boolean = flushSo();
				if(flushed){
					value = null; 
					System.gc();
				}
				else{
					throw new Error("Error writing to application storage");
				}
			}
			dispatchEvent(propE);
		}
		
		protected function shutdown(e:Event=null):String{
			var flushStatus:String = null;
			try {
				flushStatus = locSo.flush(1000);
			} catch (error:Error) {
				flushStatus = error.message;
			}
			return flushStatus;
		}
		
		public function hasEventListener(type:String):Boolean { return dispatcher.hasEventListener(type); }
		public function willTrigger(type:String):Boolean { return dispatcher.willTrigger(type); }
		public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0.0, useWeakReference:Boolean=false):void { dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference); }
		public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void { dispatcher.removeEventListener(type, listener, useCapture); }
		public function dispatchEvent(event:Event):Boolean { return dispatcher.dispatchEvent(event); }
		
		public static var webBased:Boolean;
		
		protected static var dispatcher:EventDispatcher;
		//protected static var notifier:NotificationManager;
		public var version:String;
		public var versionLabel:String;
		public var appID:String;
		private var locSo:SharedObject;
		private var locSoPath:String;
	}
}